/* #include  */
#include <pthread.h>
#include "libvid.h"
/* #include "debug.h" */

/* video			*current = NULL; */


/* value			video_main(value		ba) */
/* { */
/*   pthread_t		p[3]; */
/*   int			sound_arg[1]; */
/*   int			video_arg[1]; */
/*   int			core_arg[1]; */


/*   pthread_create(&p[1], NULL, (void*) th_sound, NULL); */
/*   pthread_create(&p[2], NULL, (void*) th_video, NULL); */
/*   pthread_create(&p[3], NULL, (void*) th_core, NULL); */

/*   /\* pthread_exit(th_sound); *\/ */
/*   /\* pthread_exit(th_video); *\/ */
/*   /\* pthread_exit(th_core); *\/ */

/*   return (Val_int (0)); */
/* } */

video			*video_init(value		fichier)
{
  int			i = 0;
  int			w = 0;
  int			h = 0;
  int			format = PIX_FMT_RGB24; /* grepme useless var */
  char			*file = String_val(fichier);
  video			*ret = malloc(sizeof (video));
  int			rate = 0;
  int			channels = 0;
  pa_sample_format_t	sam_format;


  memset(ret, 0, sizeof (video));
  ret->format = format;

  /* Init ffmpeg */
  av_register_all();

  /* Open file, check usability */
  if (av_open_input_file(&ret->pFormatCtx, file, NULL, 0, NULL)
      || av_find_stream_info(ret->pFormatCtx) < 0)
    video_quit(ret);

  /* Find the first video stream */
  ret->videoStream = -1;
  ret->fluxAudio = -1;

  for (i = 0; abs(i) < ret->pFormatCtx->nb_streams; i++)
    if(ret->pFormatCtx->streams[i]->codec->codec_type == CODEC_TYPE_VIDEO)
    {
      ret->videoStream = i;
      break;
    }

  for(i = 0; abs(i) < ret->pFormatCtx->nb_streams; i++)
    if (ret->pFormatCtx->streams[i]->codec->codec_type == CODEC_TYPE_AUDIO)
    {
      ret->fluxAudio = i;
      break;
    }

  if(ret->videoStream == -1)
    video_quit(ret);

  if(ret->fluxAudio == -1)
    video_quit(ret);

  /* Get context for codec, pin down target width / height, find codec */
  ret->pCtx = ret->pFormatCtx->streams[ret->videoStream]->codec;
  ret->width = w ? w : ret->pCtx->width;
  ret->height = h ? h : ret->pCtx->height;

  ret->pAudioCodec = ret->pFormatCtx->streams[ret->fluxAudio]->codec;

  ret->pCodec = avcodec_find_decoder(ret->pCtx->codec_id);
  ret->pAudio = avcodec_find_decoder(ret->pAudioCodec->codec_id);

  if (!ret->pCtx) /* video Codec non supporté */
      video_quit(ret);

  if (!ret->pAudio) /* audio Codec non supporté*/
      video_quit(ret);

  if (!ret->pCodec
      || avcodec_open(ret->pCtx, ret->pCodec) < 0
      || avcodec_open(ret->pAudioCodec, ret->pAudio) < 0)
    video_quit(ret);

  /* Frame rate fix for some codecs */
  if (ret->pCtx->time_base.num > 1000 && ret->pCtx->time_base.den == 1)
    ret->pCtx->time_base.den = 1000;

  /* Get framebuffers */
  ret->pRaw = avcodec_alloc_frame();
  ret->pDat = avcodec_alloc_frame();

  if(!ret->pRaw || !ret->pDat)
    video_quit(ret);

  /* Create data buffer */
  ret->buffer =
    malloc(avpicture_get_size(ret->format,ret->pCtx->width, ret->pCtx->height));

  /* Init buffers */
  avpicture_fill(
    (AVPicture *) ret->pDat, ret->buffer, ret->format,
    ret->pCtx->width, ret->pCtx->height);

  /* Init scale && convert */
  ret->Sctx =
    sws_getContext(ret->pCtx->width, ret->pCtx->height, ret->pCtx->pix_fmt,
		   ret->width, ret->height, ret->format,
		   SWS_BICUBIC, NULL, NULL, NULL);

  if (!ret->Sctx)
    video_quit(ret);

  /* Give some info on stderr about the file & stream */
  /* dump_format(ret->pFormatCtx, 0, file, 0); */

  rate = ret->pAudioCodec->sample_rate;
  channels = ret->pAudioCodec->channels;

  switch (ret->pAudioCodec->sample_fmt)
    {
    case SAMPLE_FMT_U8 : sam_format = PA_SAMPLE_U8; break;

    case SAMPLE_FMT_S16 : sam_format = PA_SAMPLE_S16LE; break;

    case SAMPLE_FMT_S32:
    default:
      sam_format = PA_SAMPLE_S32LE; break;
    }

  /* ret->video_current_pts_time = av_gettime(); */
  /* ret->audio_buf_size = 99506; /\* grepme *\/ */
  /* /\* ret->av_sync_type = DEFAULT_AV_SYNC_TYPE; *\/ */

  ret->pass = initserver(sam_format, rate, channels);

  return (ret);
}


/* double get_audio_clock(video *is) { */
/*   double pts; */
/*   int hw_buf_size, bytes_per_sec, n; */

/*   pts = is->audio_clock; /\* maintained in the audio thread *\/ */
/*   hw_buf_size = is->audio_buf_size - is->audio_buf_index; */
/*   bytes_per_sec = 0; */
/*   n = is->audio_st->codec->channels * 2; */
/*   if(is->audio_st) { */
/*     bytes_per_sec = is->audio_st->codec->sample_rate * n; */
/*   } */
/*   if(bytes_per_sec) { */
/*     pts -= (double)hw_buf_size / bytes_per_sec; */
/*   } */
/*   return pts; */
/* } */

double			get_video_clock(video *is)
{
  double		delta;

  delta = (av_gettime() - is->video_current_pts_time) / 1000000.0;
  return (0/* is->video_current_pts + delta */);
}


void			stream_seek(video			*is,
				    int64_t			pos,
				    int				rel)
{
  /* pos = 1; */

  if (!is->seek_req)
  {
    is->seek_pos = pos;
    is->seek_flags = rel < 0 ? AVSEEK_FLAG_BACKWARD : 0;
    is->seek_req = 1;
  }
}

value			video_next(video			*cur)
{

  AVPacket		pack;
  int16_t		audio_buf[99506/* AVCODEC_MAX_AUDIO_FRAME_SIZE */]; /* grepme */
  /* int16_t		*audio_buf = calloc(sizeof (int16_t), AVCODEC_MAX_AUDIO_FRAME_SIZE); */
  int			finished = 0;
  int			len;
  int			i;
  int			audio_buf_size;
  int			stream_index;
  int64_t		seek_target;

  /* printf("video_next\n"); */
  /* printf("%i\n", cur[2]); */


  if (cur->seek_req)
    {
      stream_index= -1;
      seek_target = cur->seek_pos;

      if  (cur->videoStream >= 0)
  	stream_index = cur->videoStream;
      else
  	if(cur->fluxAudio >= 0)
  	  stream_index = cur->fluxAudio;

      if(stream_index>=0)
      	{
      	  seek_target= av_rescale_q(seek_target, AV_TIME_BASE_Q,
      				    cur->pFormatCtx->streams[stream_index]->time_base);
      	}
      if(av_seek_frame(cur->pFormatCtx, stream_index,
  		       seek_target, cur->seek_flags) < 0)
  	{
  	  fprintf(stderr, "%s: error while seeking\n",
  		  cur->pFormatCtx->filename);
  	}
    }

  /* Can we read a frame? */
  if (av_read_frame(cur->pFormatCtx, &pack) < 0)
    return (Val_int(-1));

  /* Is it what we're trying to parse ? */
  if (pack.stream_index == cur->videoStream || pack.stream_index == cur->fluxAudio)
  {
    if (pack.stream_index == cur->videoStream)
    {
      /* Decode it! */
      avcodec_decode_video2(cur->pCtx, cur->pRaw, &finished, &pack);

      /* Succes? If not, drop packet. */
      if (!finished)
      {
	av_free_packet(&pack);
	return Val_int(-3);
      }
    }
    else
    {
      if (pack.stream_index == cur->fluxAudio)
      {
	/* while (pack.size > 0) */
	/* 	{  */
	/* av_init_packet(&pack); */
	audio_buf_size = sizeof audio_buf;

	len = avcodec_decode_audio3(cur->pAudioCodec,
				    audio_buf,
				    &audio_buf_size,
				    &pack);

	if ((i = sendserver(cur->pass,audio_buf,audio_buf_size)) == -1)
	{
	  printf ("send server error\n");
	  return(-1);
	}
	av_free_packet(&pack);
	return Val_int(-3);
      }
    }
  }
  else
  {
    av_free_packet(&pack);
    return Val_int(-2);
  }

  /* gcc gets trippy because of the double const usage in argument 2 - hard cast
   * Scale & convert decoded frame to correct size & colorspace
   */
  sws_scale(cur->Sctx, (const uint8_t* const *) cur->pRaw->data,
	    cur->pRaw->linesize, 0, cur->pCtx->height,
	    cur->pDat->data, cur->pDat->linesize);

  av_free_packet(&pack);

  return (Val_int(0));
}


/*
 * Close & Free cur video context
 * This function is also called on failed init
 * so check existence before de-init
 */

value			video_quit(video			*cur)
{

  if(cur->Sctx)
    sws_freeContext(cur->Sctx);

  if(cur->pRaw)
    av_free(cur->pRaw);
  if(cur->pDat)
    av_free(cur->pDat);

  if(cur->pCtx)
    avcodec_close(cur->pCtx);
  if(cur->pFormatCtx)
    av_close_input_file(cur->pFormatCtx);

  free(cur->buffer);
  free(cur);

  return (0);
}


value			width(video			*cur)
{
  return (Val_int(cur->width));
}


value			height(video			*cur)
{
  return (Val_int(cur->height));
}



value			pixel(video			*cur,
			      value			i,
			      value			j)
{
  int			colone = Int_val(i);
  int			ligne = Int_val(j);
  uint8_t		*ln = cur->pDat->data[0] + ligne * cur->pDat->linesize[0];
  int			r = (int) ln[3 * colone];
  int			v = (int) ln[3 * colone + 1];
  int			b = (int) ln[3 * colone + 2];
  int			res = r * 16 * 16 * 16 * 16 + v * 16 * 16 + b;

  return Val_int(res);
}


value			addchar(value			str,
				value			x,
				value			y)
{
  char			ch = String_val(str)[0];

  mvaddch(Int_val(y),Int_val(x),ch);
  return (0);
}

value			curse_aff(value			str,
				  value			colored,
				  value			col)
{
  char			ch = String_val(str)[0];

  if (Int_val(colored))
    color_set((Int_val(col) - 29), NULL);
  addch(ch);

  return 0;
}


value			end_curse(value			unused);


value			refr(value			unused)
{
  unused = unused;
  refresh();

  return (0);
}

value			mv_init(value			unused)
{
  unused = unused;
  move(0,0);

  return (0);
}

value			end_curse(value			unused) /* grepme */
{
  unused = unused;
  endwin();

  return (0);
}


/* int			th_video(video		*current) */
/* { */
/*   video			*cur = current; */
/*   AVPacket		pack; */
/*   int16_t		audio_buf[AVCODEC_MAX_AUDIO_FRAME_SIZE]; /\* grepme 99506 *\/ */
/*   int			finished = 0; */
/*   int			len; */
/*   int			i; */
/*   int			audio_buf_size; */


/*   /\* Can we read a frame? *\/ */
/*   if (av_read_frame(cur->pFormatCtx, &pack) < 0) */
/*     return Val_int(-1); */

/*   /\* Is it what we're trying to parse ? *\/ */
/*   if (pack.stream_index == cur->videoStream || pack.stream_index == cur->fluxAudio) */
/*   { */
/*     if (pack.stream_index == cur->videoStream) */
/*     { */
/*       /\* Decode it! *\/ */
/*       avcodec_decode_video2(cur->pCtx, cur->pRaw, &finished, &pack); */

/*       /\* Succes? If not, drop packet. *\/ */
/*       if (!finished) */
/*       { */
/* 	av_free_packet(&pack); */
/* 	return Val_int(-3); */
/*       } */
/*     } */
/*     else */
/*     { */
/*       if (pack.stream_index == cur->fluxAudio) */
/*       { */
/* 	/\* while (pack.size > 0) *\/ */
/* 	/\* 	{  *\/ */
/* 	/\* av_init_packet(&pack); *\/ */
/* 	audio_buf_size = sizeof audio_buf; */

/* 	len = avcodec_decode_audio3(cur->pAudioCodec, */
/* 				    audio_buf, */
/* 				    &audio_buf_size, */
/* 				    &pack); */
/* 	/\* LOLILOL koukou *\/ */
/* 	if ((i = sendserver(cur->pass,audio_buf,audio_buf_size)) == -1) */
/* 	{ */
/* 	  printf ("kaikai\n"); */
/* 	  return(-1); */
/* 	} */
/* 	av_free_packet(&pack); */
/* 	return Val_int(-3); */
/*       } */
/*     } */
/*   } */
/*   else */
/*   { */
/*     av_free_packet(&pack); */
/*     return Val_int(-2); */
/*   } */

/*   /\* gcc gets trippy because of the double const usage in argument 2 - hard cast */
/*    * Scale & convert decoded frame to correct size & colorspace */
/*    *\/ */
/*   sws_scale(cur->Sctx, (const uint8_t* const *) cur->pRaw->data, */
/* 	    cur->pRaw->linesize, 0, cur->pCtx->height, */
/* 	    cur->pDat->data, cur->pDat->linesize); */

/*   av_free_packet(&pack); */

/*   return (0); */
/* } */
